﻿using System;
using System.Collections.Generic;
using System.Linq;
using Coder.Workflow;
using Coder.Workflow.Stores;
using Fee.MingTong;
using Fee.Orders.DesignWorkflow;
using Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;

namespace Fee.Orders.FormalWorkflow
{
    public class FormalOrderManager<T> : WorkflowManager<T, FormalOrder>
        where T : FeeDbContext

    {
        private readonly T _context;
        private readonly ILogger<FormalOrderManager<T>> _logger;
        private readonly MingTongManager _mingTongManager;
        private readonly IWorkActivityStore<T> _workActivityStore;
        private readonly IWorkProcessStore<T> _workProcessStore;


        public FormalOrderManager(T context,
            IWorkProcessStore<T> workProcessStore,
            IWorkActivityStore<T> workActivityStore, MingTongManager mingTongManager,
            ILogger<FormalOrderManager<T>> logger
        ) : base(context, workActivityStore)
        {
            _context = context;

            _workProcessStore = workProcessStore;
            _workActivityStore = workActivityStore;
            _mingTongManager = mingTongManager;
            _logger = logger;
        }

        protected override IQueryable<WorkActivity> FillQuery(IQueryable<WorkActivity> workActivities)
        {
            return workActivities
                .Include(f => (BudgetOrder) f.ProcessInstance).ThenInclude(f => f.Details).ThenInclude(f => f.Project)
                .Include(f => (BudgetOrder) f.ProcessInstance).ThenInclude(f => f.Details).ThenInclude(f => f.Contract)
                .ThenInclude(f => f.Vendor)
                .Include(f => (BudgetOrder) f.ProcessInstance).ThenInclude(f => f.Details).ThenInclude(f => f.Contract)
                .Include(f => (BudgetOrder) f.ProcessInstance).ThenInclude(f => f.PlanCosts)
                .Include(f => (BudgetOrder) f.ProcessInstance).ThenInclude(f => f.ConstructionTask)
                .ThenInclude(f => f.Station).ThenInclude(f => f.District)
                .Include(f => (BudgetOrder) f.ProcessInstance).ThenInclude(f => f.ConstructionTask)
                .ThenInclude(f => f.Station).ThenInclude(f => f.OrderDistrict)
                .Include(f => (BudgetOrder) f.ProcessInstance).ThenInclude(f => f.ConstructionTask)
                .ThenInclude(f => f.Details).ThenInclude(f => f.Contract).ThenInclude(f => f.Vendor)
                .Include(f => (BudgetOrder) f.ProcessInstance).ThenInclude(f => f.ConstructionTask)
                .ThenInclude(f => f.PlanCosts)
                .Include(f => (BudgetOrder) f.ProcessInstance).ThenInclude(f => f.PlanCosts);
        }

        public override FormalOrder GetById(int id)
        {
            return Context
                .Set<FormalOrder>()
                .Include(f => f.Details)
                .Include(f => f.WorkProcess).ThenInclude(f => f.Items)
                .Include(f => f.User)
                .Include(f => f.Details).ThenInclude(f => f.Project)
                .Include(f => f.Details).ThenInclude(f => f.Contract).ThenInclude(f => f.Vendor)
                .Include(f => f.Details).ThenInclude(f => f.Contract)
                .Include(f => f.PlanCosts)
                .Include(f => f.ConstructionTask).ThenInclude(f => f.Station).ThenInclude(f => f.District)
                .Include(f => f.ConstructionTask).ThenInclude(f => f.Station).ThenInclude(f => f.OrderDistrict)
                .Include(f => f.ConstructionTask).ThenInclude(f => f.Details).ThenInclude(f => f.Contract).ThenInclude(f => f.Vendor)
                .Include(f => f.ConstructionTask).ThenInclude(f => f.PlanCosts)
                .FirstOrDefault(f => f.Id == id);
        }

        public FormalOrder CreateOrTryToStart(ConstructionTask srcTask, User user)
        {
            if (srcTask == null) throw new ArgumentNullException(nameof(srcTask));

            if (user == null) throw new ArgumentNullException(nameof(user));

            if (srcTask.Station.Status != StationStatus.S3)
                throw new WorkflowException("站点只有处于's3'才能发起正式委托");


            var handByAnother = _context.Database.CurrentTransaction != null;
            var trans = handByAnother ? null : _context.Database.BeginTransaction();
            try
            {
                var wf = _workProcessStore.Get<FormalOrderWorkProcess>();
                var formal = _context.Set<FormalOrder>()
                    .FirstOrDefault(f => f.ConstructionTask.Id == srcTask.Id &&
                                         f.Status != ProcessInstanceStatus.Completed &&
                                         f.Status != ProcessInstanceStatus.Canncelled);
                formal = formal ?? new FormalOrder(wf, user)
                {
                    ConstructionTask = srcTask,
                    User = user
                };

                foreach (var taskDetail in srcTask.Details)
                {
                    var orderDetail = taskDetail.CreateOrderDetail();
                    formal.Details.Add(orderDetail);
                }

                foreach (var costDetail in srcTask.PlanCosts) formal.PlanCosts.Add(new FormalOrderPlanCost(costDetail));
                //formal.ThirdPartyWorkflowId = srcTask.Station.ThirdPartyWorkOrderId;
                _context.Update(formal);

                _context.SaveChanges();
                if (srcTask.Status == ConstructionTaskStatus.Designed && formal.Status == ProcessInstanceStatus.Created)
                    Start(formal);

                trans?.Commit();
                return formal;
            }
            catch
            {
                trans?.Rollback();
                throw;
            }
            finally
            {
                trans?.Dispose();
            }
        }


        public override IEnumerable<WorkActivity> Start(FormalOrder order)
        {
            if (order.ConstructionTask == null)
            {
                _context.Entry(order).Reference(a => a.ConstructionTask).Load();
                if (order.ConstructionTask == null)
                    throw new ArgumentOutOfRangeException();
            }

            var srcTask = order.ConstructionTask;
            if (srcTask.Status != ConstructionTaskStatus.Designed) throw new WorkflowException("只有'完成设计后'才能发起正式委托");
            order.ConstructionTask.Status = ConstructionTaskStatus.FormalAuthing;
            return base.Start(order);
        }

        protected override void OnProcessComplete(ProcessInstance instance)
        {
            var order = (FormalOrder) instance;
            order.ConstructionTask.Status = ConstructionTaskStatus.FormalAuthed;

            base.OnProcessComplete(instance);
            if (order.ThirdPartyWorkflowId != null)
            {
                _logger.LogInformation($"正式委托流程结束,{instance.Number} 向铭通发送通知");

                _mingTongManager.FormalOrderSubmit(order);
            }
        }

        public IEnumerable<WorkActivity> GetMyTask(DelegateWorkActivitySearch searcher, out int total)
        {
            var query = searcher.ForDelegateBase(_context);

            total = query.Count();

            query = _workActivityStore.IncludeAllDelegateBase(query);

            return query.Skip(searcher.GetSkip()).Take(searcher.GetTake()).ToList();
        }

        protected override IEnumerable<WorkActivity> StartProcess(FormalOrder order)
        {
            order.ConstructionTask.Status = ConstructionTaskStatus.FormalAuthing;
            return base.StartProcess(order);
        }
    }
}